<?php

    /* Mini Shoutcast based auto dj with hot rotation functionality
     * Matthias Kallenbach, 2014, Berlin
     * 
     * tested on windows 7, LAMP (xampp)
     * 
     * php.ini
     *      error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED
     *      short_open_tag = On
     *      max_execution_time = 1000 
     *      
     * 
     * .htaccess
     *      <IfModule mod_rewrite.c>
     *          RewriteEngine On
     *          RewriteBase /
     *          RewriteCond %{REQUEST_FILENAME} !-f
     *          RewriteCond %{REQUEST_FILENAME} !-d
     *          RewriteRule . index.php [L]
     *      </IfModule>  
     * 
     */
     
    class Playlist {
        
        /**
         * 
         * 
         */
        public function generate($slug=false){
            
            //
            if(!$slug)
                return;
            
            $show = $this->getShowJson($slug);
            $this->build($show,$podcast);
        }
        
        /**
         * 
         * 
         */
        public function build($show=false){
            global $suffix;
            
            $dir = $show['folder'];
            
            // get the song files
            $files = $this->searchFiles($dir, false);
            
            // get the podcast files
            if($show['podcast']){
                include_once('source/assets/ClassPodcast.php');
                $pc             = new Podcast();
                $podcast        = $pc->getFeed($show['podcast']['slug']);
                
                if($podcast['folder'])
                    $podcast_files  = array_values($this->searchFiles($podcast['folder']));
            }
            
                        
            // get spot files
            $spot_files = array_values($this->searchFiles($show['folder'].'/spot'));
            
             // get intro files
            $intro_files = array_values($this->searchFiles($show['folder'].'/intro'));

            $playlist = array();
            $i=0;
            foreach($files as $f){
                $playlist[] = str_replace('\\','/',$f['path']).'/'.$f['rname'];
                if($i==MAX_PLAYLIST_ENTRIES)
                    break;
                
                $i++;
            }
 
            // get hot rotation
            if($show['hot_rotation']['song_num']>0){
                    
                $i=0;
                $hot_rotation = array();
                foreach($playlist as $p){
                    if($i==$show['hot_rotation']['song_num'])
                        break;
                    
                    $hot_rotation[] = $p;
                    $i++;
                }
                
                // shuffle beginning hot rotation
                if($show['hot_rotation']['shuffle_beginning']==1)
                    shuffle($hot_rotation);
                
                // multiply the hot rotation
                $hot_multi = array();
                for($i=0; $i < $show['hot_rotation']['insert_multiplier']; $i++)
                    $hot_multi = array_merge($hot_rotation,$hot_multi);
                
                // merge muliplied hot rotation with sorted playlist
                $playlist = array_merge($playlist,$hot_multi);
                
            }
            
            // shake it
            shuffle($playlist);
            
            // add hot rotation at the beginning
            if($show['hot_rotation']['at_beginning']==1)
                $playlist = array_merge($hot_rotation,$playlist);

            //
            if($podcast!=false){
                $index  = 0;    
                $pnum   = 0;
                
                // add random podcast on top
                if($show['podcast']['random_first']==1){
                    $index = rand(0, ($show['podcast']['cast_num']-1) );
                    array_unshift($playlist,$podcast_files[$index]['path'].'/'.$podcast_files[$index]['filename']);
                    $pnum = 1;
                }
                
                // build new playlist with podcasts
                $every_nth_song = $show['podcast']['every_nth_song'];
                if($every_nth_song>0){
                    $playlist_with_podcast = array();    
                    $i = 0;
                    foreach($playlist as $p){
                        if( ($i+($every_nth_song-$show['podcast']['offset'])) % ($every_nth_song) === 0 ){
                                
                            $index = rand(0,$show['podcast']['cast_num']-1);
                            
                            if($show['podcast']['order_by_time_desc']==1){
                                $index = $pnum%$show['podcast']['cast_num'];
                            }
                            
                            if(($i>0 && $show['podcast']['random_first']==1) || $show['podcast']['random_first']!=1){
                                $playlist_with_podcast[] = $podcast_files[$index]['path'].'/'.$podcast_files[$index]['filename'];
                                $pnum++;
                            }
                        }
                        
                        $playlist_with_podcast[] = $p;  
                        $i++;
                    }
                    $playlist = $playlist_with_podcast;
                }

                // add spots
                if(count($spot_files)>0){
                    $playlist_with_spots    = array();
                    $spot_num               = count($spot_files);
                    $i = 0;
                    foreach($playlist as $p){
                        $index = rand(0,$spot_num-1);
                        if( ($i+($show['spot']['every_nth_song']-$show['spot']['offset'])) % ($show['spot']['every_nth_song']) === 0 ){
                            $playlist_with_spots[] = $spot_files[$index]['path'].'/'.$spot_files[$index]['filename'];
                        }
                        $playlist_with_spots[] = $p;
                        $i++;
                    }
                    $playlist = $playlist_with_spots;
                }
                
                // add opener
                if(count($intro_files)>0){
                    $index = 0;
                    if($show['intro']['random']==1)
                        $index = rand(0,count($intro_files)-1);
                    
                    array_unshift($playlist,$intro_files[$index]['path'].'/'.$intro_files[$index]['filename']);
                }
                
            }
            
            
            
            // write the playlist file
            $fh = fopen(PLAYLIST_FILE,'w');
            
            foreach($playlist as $p)  {
         
                $split      = explode('/',$p);
                $filename   = $split[count($split)-1];
                unset($split[count($split)-1]);
                
                $path = implode('/',$split);
                
                if (strpos($path,'://') !== false) {
                    $row = str_replace('\\','/',$path).'/'.($filename)."\r\n";
                } else {
                    $row = str_replace('/','\\',($path.'/'.mb_convert_encoding($filename,'UTF-8','auto'))."\r\n");
                }
                
                echo $row."";
                
                fwrite($fh,$row);       
            }
            fclose($fh);
        }

        /*
         * 
         * 
         */
         public function scheduleCronjob(){
            
            // shows
            $shows      = $this->getShowJson();
            
            // timetable
            $schedule   = $this->getScheduleJson();
            
            $now = array(
                'weekday'   =>intval(date('N')),
                'hour'      =>intval(date('H')),
                'minute'    =>intval(date('i'))
            );
            
            // fishing the actual scheduled show 
            
            $now_playing = false;
                       
            foreach($schedule as $t){
                $is = $this->isWeekday($t['weekday'], $now);
                
                if($is===true && intval($t['hour'])==$now['hour'] && intval($t['minute'])==$now['minute']){
                    $now_playing = $shows[$t['show']];
                }
            }
                        
            if($now_playing!=false){
                $this->generate($now_playing['slug']);
                $this->setNowPlaying($now_playing['slug']);
                $this->reloadPlaylist($now_playing['name']);
            } else {
                echo "Nothing to do at ".date('H:i')."\n";
            }

            return array(
                            'now_playing'   => $now_playing,
                            'schedule'      => $schedule,
                            'show'          => $shows
                        );
            
        }
         
        /**
         * 
         * 
         * 
         */
        function searchFiles($dir, $search = false){
            global $suffix,$excludes;
            
            if($dir){
                $path = ($dir);
            } else {
                $path = PATH_ROOT;
            }
        
            
            $files      = array();
            //$objects    = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
            
            // because excluding spot and intro folder inside this folder...
            $objects = new DirectoryIterator($path);
            
            
            foreach($objects as $name => $object){
                $ok=false;  
                if($search!='')
                    if( strpos(strtolower($object->getFilename()),strtolower($search))!=false && !$object->isDir() && !in_array( $object->getFilename(),$excludes ))
                        $ok=true;
                
                if($search=='')
                    if(!$object->isDir() && !in_array( $object->getFilename(), $excludes ))
                        $ok=true;
                    
                if( $ok==true ){    
                    $name = utf8_encode($object->getFilename());
                    
                    $files[] = array(
                                    'name'      => str_replace($suffix,'',$name),
                                    'rname'     => utf8_encode($object->getFilename()),
                                    'filename'  => utf8_encode($object->getFilename()),
                                    'path'      => $object->getPath(),
                                    'ts_edit'   => 'te'.$object->getMTime().microtime(),
                                    'ts_make'   => 'tm'.$object->getCTime().microtime()
                                    );
                }
            }
            
            $files = array_reverse(ksortBy($files, 'ts_make'));
            
            return $files;
        }
        
        /**
         * 
         * 
         */
        public function getShowJson($slug=false){
            $show_json = file(SHOW_JSON);
            $show_file = implode($show_json);
            $show      = json_decode($show_file,true);
            
            
            $shows = array();
            foreach($show as $s){
                $shows[$s['slug']] = $s;
            }
            
            if($slug==false) {
                return $shows;
            } else {
                return $shows[$slug];
            }
        }
        
        public function getScheduleJson($with_duration=false){
            $schedule_json = file(SCHEDULE_JSON);
            $schedule_file = implode($schedule_json);
            $schedule      = json_decode($schedule_file,true);

            if($with_duration==true){
                return $this->buildTimetable($schedule);
            }

            return $schedule;
            
        }

        public function buildTimetable($schedule){
            $weekday            = date('N');   
            $weekday_yesterday  = date('N',strtotime('yesterday'));    
            $weekday_tomorrow   = date('N',strtotime('tomorrow'));   
            
            $sort   = array(
                'today'     => array(),
                'yesterday' => array(),
                'tomorrow'  => array(),
            );
            
            foreach($schedule as $s){
                
                // today    
                if($this->isWeekday($s['weekday'])){
                    $key = 's'.$weekday.fillLeadingZero($s['hour']).fillLeadingZero($s['minute']);    
                    $sort['today'][$key] = json_decode(json_encode($s), true);
                    $sort['today'][$key]['weekday_set'] = $weekday;
                }
                
                // yesterday
                if($this->isWeekday($s['weekday'],array('weekday'=>$weekday_yesterday))){
                    $key = 's'.$weekday_yesterday.fillLeadingZero($s['hour']).fillLeadingZero($s['minute']);    
                    $sort['yesterday'][$key] = json_decode(json_encode($s), true);
                    $sort['yesterday'][$key]['weekday_set'] = $weekday_yesterday;
                }
                
                // tomorrow
                if($this->isWeekday($s['weekday'],array('weekday'=>$weekday_tomorrow))){
                    $key = 's'.$weekday_tomorrow.fillLeadingZero($s['hour']).fillLeadingZero($s['minute']);    
                    $sort['tomorrow'][$key] = json_decode(json_encode($s), true);
                    $sort['tomorrow'][$key]['weekday_set'] = $weekday_tomorrow;
                }
                
            
            }
            
            ksort($sort['today']);
            ksort($sort['yesterday']);
            ksort($sort['tomorrow']);

            $sort['flow'] = array_merge(array_slice($sort['yesterday'], -1, 1), $sort['today'], array_slice($sort['tomorrow'], 0, 1) );
            
            $timeline = array_values($sort['flow']);
           
            
            $i=0;
            foreach($timeline as $t){
                $timeline[$i]['start_ts'] = strtotime('today '.$t['hour'].'.'.$t['minute']);
                $timeline[$i]['end_ts']   = strtotime('today '.$timeline[$i+1]['hour'].'.'.$timeline[$i+1]['minute']); 
                    
                if($t['hour'].$t['minute']!='00' && $i===0){
                   $timeline[$i]['start_ts'] = strtotime('yesterday '.$t['hour'].'.'.$t['minute']);
                  
                }
                
                if($t['hour'].$t['minute']!='00' && $i===count($timeline)-1){
                   $timeline[$i]['start_ts'] = strtotime('tomorrow '.$t['hour'].'.'.$t['minute']);
                }
                
                if($timeline[$i]['hour']>$timeline[$i+1]['hour'] && $i>0){
                    $timeline[$i]['end_ts'] = strtotime('tomorrow '.$timeline[$i+1]['hour'].'.'.$timeline[$i+1]['minute']);
                    
                }
                
                $timeline[$i]['start_date'] = date('d.m.Y H:i', $timeline[$i]['start_ts']); 

                $timeline[$i]['end_date'] = date('d.m.Y H:i', $timeline[$i]['end_ts']); 
                $timeline[$i]['duration'] = $timeline[$i]['end_ts'] - $timeline[$i]['start_ts'];
                $i++;
            }

            if($timeline[count($timeline)-1]['hour']<$timeline[count($timeline)-2]['hour'])
                unset($timeline[count($timeline)-1]);
            
            $all_over_duration = 0;
            foreach($timeline as $t){
                $all_over_duration+=$t['duration'];
            }
            
            foreach($timeline as $i=>$t){
                $timeline[$i]['percent'] = (100/$all_over_duration)*$t['duration'];
            }
            
            
            
            $sort['flow'] = $timeline;
            $sort['all_over_duration'] = $all_over_duration;
            
            if($sort['flow'][0]['weekday_set']!=$sort['flow'][1]['weekday_set']){
               
                $ts_start   = $sort['flow'][0]['start_ts'];
                $ts_end     = strtotime('yesterday 23.59.59' );
                
                $sort['yesterday_duration'] = $ts_end - $ts_start;
            }
            
            return $sort;
        }

        public function buildTimelineWeek($schedule){
            
            $monday = date('d.m.Y H:i',mondayFromCalendarWeek(date('W'),date('Y')));
            $sort   = array();
            for($i=0; $i<7; $i++){
                $weekday            = $i+1;
                $day                = strtotime('+'.$i.' day', strtotime($monday));
                $date               = date('d.m.Y',$day);
                
                $weekday_yesterday  = date('N',strtotime('-1 day', $day));    
                $weekday_tomorrow   = date('N',strtotime('+1 day', $day));
                
                foreach($schedule as $s){
                    if($this->isWeekday($s['weekday'], array('weekday'=>$weekday))){
                        $key = 's'.fillLeadingZero($s['hour']).fillLeadingZero($s['minute']);    
                        $sort[$i][$key] = json_decode(json_encode($s), true);
                    }
                }
                
                ksort($sort[$i]);
                
                $sort[$i] = array_values($sort[$i]);
            }
            
            //
            $grand_duration = 0;
            $grand_start    = false;
            for($i=0; $i<7; $i++){
                $day                = strtotime('+'.$i.' day', strtotime($monday));
                $date               = date('d.m.Y',$day);
                
                foreach($sort[$i] as $key=>$s){
                    $sort[$i][$key]['start_ts'] = strtotime($date.' '.$sort[$i][$key]['hour'].'.'.$sort[$i][$key]['minute']);
                    $sort[$i][$key]['end_ts']   = strtotime($date.' '.$sort[$i][$key+1]['hour'].'.'.$sort[$i][$key+1]['minute']);
                    
                    if($key==count($sort[$i])-1 && $sort[$i][$key]['end_ts'] < $sort[$i][$key]['start_ts']){
                        if($i<6){
                            $tday = strtotime('+'.($i+1).' day', strtotime($monday));
                            $ni = $i+1;
                        } else {
                            $tday = strtotime('+7 day', strtotime($monday));
                            $ni = 0;
                        }
                        $tdate  = date('d.m.Y',$tday);
                        $sort[$i][$key]['end_ts']   = strtotime($tdate.' '.$sort[$ni][0]['hour'].'.'.$sort[$ni][0]['minute']);
                    }
                          
                    $sort[$i][$key]['start_date']   = date('d.m.Y H:i',$sort[$i][$key]['start_ts']);
                    $sort[$i][$key]['end_date']     = date('d.m.Y H:i',$sort[$i][$key]['end_ts']);
                    $sort[$i][$key]['duration']     = $sort[$i][$key]['end_ts'] - $sort[$i][$key]['start_ts'];
                    
                    $match = fillLeadingZero($sort[$i][$key]['hour']).':'.fillLeadingZero($sort[$i][$key]['minute']);
                    
                    if($grand_start>$match || $grand_start==false){
                        $grand_start = $match;
                    }
                }

                $gd = $sort[$i][count($sort[$i])-1]['end_ts']-$sort[$i][0]['start_ts'];
                if($grand_duration<$gd){
                    $grand_duration = $gd;
                }
                
                
            }
            
            for($i=0; $i<7; $i++){
                foreach($sort[$i] as $key=>$s){
                    $sort[$i][$key]['percent'] = (100/$grand_duration)*$sort[$i][$key]['duration'];
                }
            }
            
            
            $offset         = array();
            $grand_start_x  = explode(':',$grand_start);
            for($i=0; $i<7; $i++){
                $day                = strtotime('+'.$i.' day', strtotime($monday));
                $date               = date('d.m.Y',$day);

                $show_time = strtotime($date.' '.$sort[$i][0]['hour'].'.'.$sort[$i][0]['minute']);
                $grand_time= strtotime($date.' '.$grand_start_x[0].'.'.$grand_start_x[1]);
                
                if($show_time>$grand_time){
                    $diff   = $show_time-$grand_time;
                    
                    $ni=$i-1;
                    if($i==0){
                        $ni=6;
                    }
                    
                    $show_before = $sort[$ni][count($sort[$ni])-1];
                    
                    $spacer = array(
                                        'start_ts' => $grand_time,
                                        'end_ts'   => $show_time,
                                        'percent'  => (100/$grand_duration)*$diff,
                                        'hour'     => $grand_start_x[0],
                                        'minute'   => $grand_start_x[1],
                                        'show'     => $show_before['show'],
                                        'class'    => 'tail'
                                        
                    );
                    array_unshift($sort[$i] , $spacer);
                }
            }
            
            
            return $sort;
        } 

        /**
         * 
         * 
         */
        public function reloadPlaylist($name){
            echo strip_tags($this->getRemoteUrl(TRANSCODER_BASE.'loadplaylist','',''))." Load Playlist\n";
            sleep(1);
            echo strip_tags($this->getRemoteUrl(TRANSCODER_BASE.'nextsong','',''))." Next Song\n";
            
            echo "Playlist Change Complete!\n";
            echo 'Now playing: '.$name."\n";
        }
        
        
        /**
         * 
         * 
         */
        public function getRemoteUrl($url,$user,$pass) {
            $header = array(
                'http' => array(
                    'header' => 'User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0' . "\r\n" . 
                                'Authorization: Basic ' . base64_encode( $user . ':' . $pass ) . "\r\n",
                ),
            );
            $context = stream_context_create( $header );
            $data = file_get_contents( $url, false, $context );
            
            return $data;
        }
        
        /**
         * 
         * 
         */
        public function setNowPlaying($slug){
            $fh = fopen(NOW_PLAYING_FILE,'w');
            fwrite($fh,$slug);       
            fclose($fh);
        }
        
        /**
         * 
         * 
         */
        public function getNowPlaying(){
            $slug   = file(NOW_PLAYING_FILE);
            $slug   = implode($slug);
            $shows  = $this->getShowJson();
            return $shows[$slug];
        }
        
        public function isWeekday($weekday,$date=false){
            
            if($date == false){
                $date = array(
                    'weekday' => intval(date('N')),
                );
            }
            
            $weekdays = explode(',',$weekday);

            $is       = false;
            if(is_array($weekdays)){
                if(in_array($date['weekday'],$weekdays)){
                    $is = true;
                }
            } else {
                if($date['weekday']==intval($weekday)){
                    $is = true;
                }
            }
            return $is;
        }
        
        
    }
    
    
?>